package com.lengyu.iottransfer.gateway.tcp.daxiang;

import com.alibaba.fastjson.JSONObject;
import com.lengyu.iottransfer.gateway.common.ByteUtils;
import com.lengyu.iottransfer.gateway.tcp.TcpServer;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;

@Component
public class BootNettyChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter {
    private static Logger logger = LoggerFactory.getLogger(BootNettyChannelInboundHandlerAdapter.class);

    @Autowired
    private DaxiangGuanpianTransfer daxiangGuanpianTransfer;

    public static BootNettyChannelInboundHandlerAdapter bootNettyChannelInboundHandlerAdapter;

    private static ConcurrentHashMap<String ,ChannelHandlerContext> ctxMap = new ConcurrentHashMap<>();

    public static ChannelHandlerContext getCtx(String deviceCode) {
        logger.info("通道列表：" + JSONObject.toJSONString(ctxMap.keySet()));
        logger.info("需要的设备编码:" + deviceCode);
        if(ctxMap.containsKey(deviceCode)){

//            if(ctxMap.get(deviceCode)){
//                logger.info("tcp通道已经失效，从map中删除");
//                ctxMap.remove(deviceCode);
//                return null;
//            }
//            else{
     ;
                return ctxMap.get(deviceCode);
//            }

        }
        else{
            return null;
        }
    }

    @PostConstruct
    public void init(){
        bootNettyChannelInboundHandlerAdapter = this;
    }

    /**
     * 从客户端收到新的数据时，这个方法会在收到消息时被调用
     *
     * @param ctx
     * @param msg
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception, IOException
    {
        try{
            if(msg != null){
                byte[] byteArray = (byte[])msg;
                logger.info("预埋TCP：channelRead:read msg:"+ByteUtils.byteArrayToHexString(byteArray));

                String start = ByteUtils.byteArrayToHexString(byteArray,0,2,false);
                logger.info("startData:" + start);

                String deviceCode = bootNettyChannelInboundHandlerAdapter.daxiangGuanpianTransfer.transforToMqttData(byteArray,ctx);
                if(StringUtils.isNotBlank(deviceCode)){
                    logger.info("大镶嵌物联发送通道添加 :" + deviceCode.substring(0,4));
                    ctxMap.put(deviceCode.substring(0,4),ctx);
                }


            }
        }

        catch (Exception ex){
            logger.error("error",ex);
        }
    }

    /**
     * 从客户端收到新的数据、读取完成时调用
     *
     * @param ctx
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws IOException
    {
        logger.info("channelReadComplete");
        ctx.flush();
    }

    /**
     * 当出现 Throwable 对象才会被调用，即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时
     *
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws IOException
    {
        logger.info("exceptionCaught");
        cause.printStackTrace();
        ctx.close();//抛出异常，断开与客户端的连接
    }

    /**
     * 客户端与服务端第一次建立连接时 执行
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception, IOException
    {
        super.channelActive(ctx);
        ctx.channel().read();
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = insocket.getAddress().getHostAddress();
        //此处不能使用ctx.close()，否则客户端始终无法与服务端建立连接
        logger.info("channelActive:"+clientIp+ctx.name());
    }

    /**
     * 客户端与服务端 断连时 执行
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception, IOException
    {
        super.channelInactive(ctx);
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = insocket.getAddress().getHostAddress();

        String removeKey = "";
        for (String i : ctxMap.keySet()) {
            ChannelHandlerContext tmpCtx = ctxMap.get(i);
            InetSocketAddress tmpInsocket = (InetSocketAddress) ctx.channel().remoteAddress();
            String tmpClientIp = tmpInsocket.getAddress().getHostAddress();
            if (clientIp.equals(tmpClientIp)) {
                removeKey = i;
                break;
            }
        }
        if(StringUtils.isNotBlank(removeKey)){
            //从map中删除相应的ctx
            ctxMap.remove(removeKey);
        }
        ctx.close(); //断开连接时，必须关闭，否则造成资源浪费，并发量很大情况下可能造成宕机
        logger.info("channelInactive:"+clientIp);
    }

    /**
     * 服务端当read超时, 会调用这个方法
     *
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception, IOException
    {
        super.userEventTriggered(ctx, evt);
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = insocket.getAddress().getHostAddress();
        ctx.close();//超时时断开连接
        logger.info("userEventTriggered:"+clientIp);



        String socketString = ctx.channel().remoteAddress().toString();

        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                logger.info("Client: "+socketString+" READER_IDLE 读超时");
                ctx.disconnect();
            } else if (event.state() == IdleState.WRITER_IDLE) {
                logger.info("Client: "+socketString+" WRITER_IDLE 写超时");
                ctx.disconnect();
            } else if (event.state() == IdleState.ALL_IDLE) {
                logger.info("Client: "+socketString+" ALL_IDLE 总超时");
                ctx.disconnect();
            }
        }
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception{
        logger.info("channelRegistered");
        //往channel map中添加channel信息
        TcpServer.channelMap.put(getIPString(ctx), ctx.channel());
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception{
        logger.info("channelUnregistered");
        //往channel map中删除channel信息
        TcpServer.channelMap.remove(getIPString(ctx));
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception{
        logger.info("channelWritabilityChanged");
    }


    public static String getIPString(ChannelHandlerContext ctx){
        String ipString = "";
        String socketString = ctx.channel().remoteAddress().toString();
        int colonAt = socketString.indexOf(":");
        ipString = socketString.substring(1, colonAt);
        return ipString;
    }


    public static String getRemoteAddress(ChannelHandlerContext ctx){
        String socketString = "";
        socketString = ctx.channel().remoteAddress().toString();
        return socketString;
    }


    private String getKeyFromArray(byte[] addressDomain) {
        StringBuffer sBuffer = new StringBuffer();
        for(int i=0;i<5;i++){
            sBuffer.append(addressDomain[i]);
        }
        return sBuffer.toString();
    }

    protected String to8BitString(String binaryString) {
        int len = binaryString.length();
        for (int i = 0; i < 8-len; i++) {
            binaryString = "0"+binaryString;
        }
        return binaryString;
    }

    protected static byte[] combine2Byte(byte[] bt1, byte[] bt2){
        byte[] byteResult = new byte[bt1.length+bt2.length];
        System.arraycopy(bt1, 0, byteResult, 0, bt1.length);
        System.arraycopy(bt2, 0, byteResult, bt1.length, bt2.length);
        return byteResult;
    }
}
